/**
 * 服务器接受客户端的点对点消息传输。
 * Created by macos on 14/11/5.
 */

var checksum = require('../checksum'),
  messageQ = require('../message-q');

(function () {

  var redisClient = Redis.client();

  /**
   * 将高位的16进制字节转化成数字
   * @param buffer
   * @returns {number}
   */
  function highHex2Number(buffer) {
    var number = buffer[0] | buffer[1] << 8 || buffer[2] << 16 | buffer[3] << 24;
    return number;
  }

  /***
   * 将数字转换成低位的字符串形式的16进制字节
   * @param number
   * @returns {*}
   */
  function number2lowHex(number) {
    var hex = number.toString(16);

    if (hex.length < 8) {
      var fillLen = 8 - hex.length;
      for (var i = 0; i < fillLen; i++) {
        hex = "0" + hex;
      }
    }

    return hex;
  }

  /**
   * 定义登录的消息协议解析。包头(2位)+类型(1位)+会话ID(6位)+长度(4位)+数据内容(等于长度)+CHK(1位)
   * @type {{event: string, type: number, content: content, checksum: checksum}}
   */
  var protocol = {
    event: 'daad', //必须放在第一位，且必须是转换成16进制的字符串
    type: 1,
    sessionId: 6,
    contentLength: function (msg, offset, buf, next) {

      var type = msg.type;
      if (type === '00') { //接受客户端提交的数据

        var contentLengthBuf = buf.slice(offset, offset + 4);
        if (contentLengthBuf.length === 4) {
          var contentLength = highHex2Number(contentLengthBuf);
          msg.contentLength = contentLength;
          offset = offset + 4;

          return next(null, msg, offset, buf);
        } else {
          return next('error contentLength', msg);
        }

      } else if (type === '01') {
        return next(null, msg, offset, buf);

      } else {
        return next('error type', msg);
      }

    },
    content: function (msg, offset, buf, next) {

      var type = msg.type;
      if (type === '00') { //接受客户端提交的数据
        var end = offset + msg.contentLength;
        var contentBuffer = buf.slice(offset, end);
        msg.contentBuffer = contentBuffer;
        offset = offset + msg.contentLength;

        if (contentBuffer.length === msg.contentLength) {
          return next(null, msg, offset, buf);
        } else {
          return next('error length of content', msg);
        }

      } else if (type === '01') { //接受客户端回复的状态
        var end = offset + 1;
        var status = buf.slice(offset, end).toString('hex');

        if (status == '01') {
          status = true;
        } else {
          status = false;
        }

        msg.status = status;
        offset = end;

        return next(null, msg, offset, buf);
      } else {
        return next('error type', msg);
      }
    },
    checksum: function (msg, offset, buf, next) { //最后一个对象的offset必须等于buf的长度

      buf = buf.slice(0, offset + 1);
      msg.checksum = checksum.checksum(buf);
      if (checksum.validate(buf, msg.checksum)) {
        offset = offset + 1;
        return next(null, msg, offset, buf);
      } else {
        return next('error checksum', msg);
      }
    }
  };


  /**
   * 回复客户端
   * @param type
   * @param sid
   * @param length
   * @param content
   * @param res
   */
  function sendMsgToClient(type, sid, length, content, res) {

    var result = {
      event: 'adda',
      type: type,
      sessionId: sid,
      len: length,
      content: content
    };

    if (length == null) delete result.len;

    res.writeObj(result, function (hexStr) {
      var buf = new Buffer(hexStr, 'hex');
      var checksumStr = checksum.checksum(buf, 0, buf.length);
      hexStr = hexStr + checksumStr;
      return hexStr;
    });
  }

  function DataHandler(server) {

    //认证成功后，可以下发数据
    server.on('authenticated', function (channel, res) {

      //开启接受数据的订阅频道
      sails.log.debug("data-protocol.DataHandler onChannel: ", channel);
      messageQ.onChannel(channel, function (err, msg) {
        if (err) {
          sails.log.error(err);
          return;
        }
        msg = JSON.parse(msg.toString());

        //校验认证状态，发送消息给客户端
        redisClient.hgetall(res.id, function (err, session) {

          if (_.has(session, 'isAuthenticated') && session.isAuthenticated === '1') {

            //发送消息给客户端
            var contentBuffer = new Buffer(msg.content);
            var lenHex = number2lowHex(contentBuffer.length);

            sails.log.debug("data-protocol.DataHandler send: ", msg);
            sendMsgToClient('00', msg.sessionId, lenHex, contentBuffer.toString('hex'), res);
          }
        });

      });

    });

    sails.log.info('onMessage:', protocol);
    server.onMessage(protocol, function (err, msg, res) {

      if (err) {
        sails.log.error(err);
        return;
      }
      sails.log.debug('msg:', msg);

      //接受TCP客户端数据
      redisClient.hgetall(res.id, function (err, session) {
        sails.log.debug('session:', session);

        if (_.has(session, 'isAuthenticated') && session.isAuthenticated === '1') {
          // 接受客户端的消息
          if (msg.type === '00') {

            //通知服务器，告之消息内容
            var receivedMsg = {fromUUID: session.uuid, sessionId: msg.sessionId, content: msg.contentBuffer.toString('hex')};
            Received.create(receivedMsg).exec(function (err, result) {
              if (err) {
                sails.log.error(err);
                //回复客户端，接受失败。
                sendMsgToClient('01', msg.sessionId, null, '00', res);
              } else {
                //回复客户端，接受成功。
                sendMsgToClient('01', msg.sessionId, null, '01', res);
              }
            });

          } else if (msg.type === '01') { //发送消息给客户端后，接受客户端回复状态。
            //更新消息状态
            var status = 2;
            if (msg.status) status = 1;
            Send.update({toUUID: session.uuid, sessionId: msg.sessionId}, {status: status}).exec(function (err, result) {
              if (err) {
                sails.log.error(err);
                return;
              }
            });

          } else {
            sendMsgToClient('01', msg.sessionId, null, '00', res)
          }

        } else {
          sails.log.debug('session timeout: ', res.id);
          res.destroy();
        }

      });
    });

  }

  module.exports = {
    runProtocol: function runProtocol(server) {
      return new DataHandler(server);
    }
  };
})();